#include	<stdlib.h>
#include	<string.h>
#include	"typedef.h"
#include	"darray.h"

#define	  DARRAY_INIT_LENGTH	20

struct _DArray
{
	void **array;
	size_t size;
	DataAllocFunc alloc_func;
	DataDestroyFunc destroy_func;
	void *ctxt;
	size_t length;
	size_t alloc_size;
};

void *darray_entry_value(DArray *thiz, size_t i)
{
	return_val_if_fail(i >= 0 && i < thiz->length, NULL);
	return thiz->array[i];
}

static void __darray_destroy_func(void *ctxt, void *data)
{
	free(data);
	data = NULL;
}

static void *__darray_alloc_func(void *ctxt, unsigned int size)
{
	return malloc(size); 
}

static Ret __darray_traverse(DArray *thiz, size_t start, size_t end, Direct direct,
				DataVisitFunc visit_func, void *ctxt)

{
	Ret ret;
	size_t i;

	if (direct == DIRECT_FORWARD) {
		for (i = start; i < end ; i++) {
			ret = visit_func(ctxt, &i);
		}
	} else {
		for (i = start; i > end ; i--) {
			ret = visit_func(ctxt, &i);
		}
	}
	return ret;

}

static Ret __darray_next_entry_swap(void *ctxt, void *i)
{
	size_t index;
	void *tmp;
	index = *(size_t *)i;
	DArray *array = (DArray *)ctxt;
	tmp = array->array[index];
	array->array[index] = array->array[index + 1];
	array->array[index + 1] = tmp; 
	return RET_OK;
}

static void __darray_shrink(DArray *thiz)
{
}

static Ret __darray_grow(DArray *thiz)
{
	size_t alloc_size;
	void **new_array;
	alloc_size = DARRAY_INIT_LENGTH + thiz->alloc_size * 2;
	new_array = (void **)malloc(alloc_size * sizeof(void *));
	return_val_if_fail(new_array != NULL, RET_OUT_OF_MEM);

	memcpy(new_array, thiz->array, sizeof(void *) * thiz->length);

	free(thiz->array);
	thiz->array = new_array;
	thiz->alloc_size = alloc_size;
	return RET_OK;
}

/**
 * @brief	
 *
 * @param	destroy_func
 * @param	cmp_func
 * @param	ctxt
 *
 * @return	
 */
DArray *darray_create(size_t size, DataAllocFunc alloc_func, DataDestroyFunc destroy_func, void *ctxt)
{
	DArray *array = (DArray *)malloc(sizeof(DArray));
	return_val_if_fail(array != NULL, NULL);

	array->size = size;
	if (alloc_func == NULL) {
		alloc_func = __darray_alloc_func;
	}
	array->alloc_func = alloc_func;
	if (destroy_func == NULL) {
		destroy_func = __darray_destroy_func;
	}
	array->destroy_func = destroy_func;
	array->ctxt = ctxt;
	array->length = 0;
	array->alloc_size = 0;
	return array;
}

/**
 * @brief	
 *
 * @param	thiz
 * @param	ctxt
 *
 * @return	
 */
void darray_destroy(DArray *thiz)
{
	size_t i;
	return_if_fail(thiz != NULL);
	for (i = 0 ; i < thiz->length ; i++) {
		thiz->destroy_func(thiz->ctxt, thiz->array[i]);
	}
	free(thiz->array);
	free(thiz);
}


Ret darray_insert(DArray *thiz, size_t i, void *data)
{
	Ret ret;
	void *_data;
	int offset;
	return_val_if_fail(i >= 0 && i <= thiz->length, RET_INVALID_PARAM);

	if (thiz->length == thiz->alloc_size) {
		ret = __darray_grow(thiz);
		return_val_if_fail(ret == RET_OK, RET_OUT_OF_MEM);
	}
	return_val_if_fail(thiz->alloc_size > thiz->length, RET_FAIL);

	_data = thiz->alloc_func(thiz->ctxt, thiz->size);
	return_val_if_fail(_data != NULL, RET_OUT_OF_MEM);
	
	memcpy(_data, data, thiz->size);

	__darray_traverse(thiz, thiz->length-1, i-1, DIRECT_BACKWARD, 
				__darray_next_entry_swap, thiz);

	thiz->array[i] = _data;
	thiz->length ++;

	return RET_OK;
}

Ret darray_delete(DArray *thiz, size_t i)
{
}

/**
 * @brief	
 *
 * @param	thiz
 * @param	cmp_func
 * @param	ctxt
 *
 * @return	
 */
Ret darray_find(DArray *thiz, size_t *index, DataCmpFunc cmp_func, void *ctxt)
{
	int i;
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAM);

	for (i = 0 ; i < thiz->length ; i++)
	{
		if (cmp_func(ctxt, thiz->array[i]) > 0) 
			break;
	}
	if (i < thiz->length) {
		*index = i;
		return RET_OK;
	}
	else return RET_FAIL;
}

/**
 * @brief	
 *
 * @param	thiz
 * @param	visit_func
 * @param	ctxt
 *
 * @return	
 */
Ret darray_for_each(DArray *thiz, DataVisitFunc visit_func, void *ctxt)
{
	size_t i;
	Ret ret = RET_OK;
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAM);

	for (i = 0 ; i < thiz->length ; i++)
	{
		ret = visit_func(ctxt, thiz->array[i]);
	}
	return ret;
}

size_t darray_count(DArray *thiz)
{
	return thiz->length;
}

size_t darray_size(DArray *thiz)
{
	return thiz->alloc_size;
}
